// 顶部：仅类型导入 + 拆分 logger 的值/类型导入
import type { ICallbackParams, IHeaders, IParams, IRequestError, RequestError } from "./request";
import Logger from "../logger";
import type { ILogger } from "../logger";
import { CONTENT_TYPE, ERROR, HEADER, METHOD, TYPE } from "./config";
import utils from "../utils";

/**
 * 使用 Fetch API 发送请求的主函数
 * @param params - 请求参数
 * @param callbackParams - 回调参数（可选）
 * @returns 返回一个Promise，解析为请求结果
 */
// 函数：sendFetchRequest
const sendFetchRequest = async function <T>(params: IParams, callbackParams?: ICallbackParams): Promise<T> {
    callbackParams = callbackParams || {};
    const logger: ILogger = new Logger(TYPE.Fetch, params.loggerLevel);

    const headers = prepareRequestHeaders(params.headers);
    // 在期望流式返回时，自动补充 SSE 头（不覆盖用户已有设置）
    if (params.responseType === 'stream') {
        if (!headers['Accept']) headers['Accept'] = 'text/event-stream';
        if (!headers['Cache-Control']) headers['Cache-Control'] = 'no-cache';
        if (!headers['Connection']) headers['Connection'] = 'keep-alive';
    }
    const { url, body } = buildRequestUrlAndBody(params, headers);

    logger.info('url', url);
    logger.info('headers', headers);
    logger.info('body', body);

    try {
        const response = await executeFetchRequest(params, url, body, headers, callbackParams, logger);

        // 流式：返回 ReadableStream；如注入解析器，使用 tee() 并并行解析事件
        if (params.responseType === 'stream') {
            logger.info('成功获取响应数据流');
            const stream = response.body as ReadableStream<Uint8Array>;
            if (!stream) return undefined as T;
            try {
                const factory = callbackParams?.sseParserFactory;
                const onEvent = callbackParams?.handleSSEEvent;
                if (factory && onEvent && typeof stream.tee === 'function') {
                    logger.info('成功创建流副本');
                    const [parseStream, returnStream] = stream.tee();
                    const reader = parseStream.getReader();
                    const decoder = new TextDecoder();
                    const parser = factory((evt: any) => onEvent(evt));

                    if (parser && typeof (parser as any).feed === 'function') {
                        (async () => {
                            try {
                                for (; ;) {
                                    const { value, done } = await reader.read();
                                    if (done) break;
                                    (parser as any).feed(decoder.decode(value, { stream: true }));
                                }
                                onEvent({ event: 'done', raw: '' });
                            } catch (e: any) {
                                onEvent({ event: 'error', data: String(e), raw: '' });
                            }
                        })();
                    } else {
                        logger.warn('SSE parser not available; skipping parsing');
                    }

                    return returnStream as T;
                } else {
                    logger.warn('未注入 SSE 解析器；保持原始流返回');
                    return stream as T;
                }
            } catch (_e) {
                // 未注入解析器：保持原始流返回
                logger.warn('未注入 SSE 解析器；保持原始流返回');
            }

            return stream as T;
        }

        // 非流式：正常处理
        const data = await handleFetchResponse<T>(response, params, logger);
        return data;
    } catch (error: any) {
        // 统一错误回调结构；Fetch 无 response.data，直接透传 error 对象
        callbackParams?.handleError?.({
            code: (typeof error?.code === 'number' ? error.code : -1),
            msg: (error instanceof Error ? error.message : 'Fetch error'),
            method: params.method,
            url,
            params: params.requestBody,
            headers,
            error: error ?? 'Unknown fetch error',
        });

        throw error;
    }
}

/**
 * 准备请求头，确保包含Content-Type
 * @param headers - 原始请求头数组
 * @returns 处理后的请求头对象
 */
function prepareRequestHeaders(headers?: IHeaders[]): Record<string, string> {
    const processedHeaders = utils.to.deepCopyArray(headers ?? []);
    const contentType = processedHeaders.find(m => m.name === HEADER.ContentType);
    if (!contentType) {
        processedHeaders.push({ name: HEADER.ContentType, value: CONTENT_TYPE.ApplicationJson });
    }
    return processedHeaders.reduce((acc, header) => {
        acc[header.name] = header.value;
        return acc;
    }, {} as Record<string, string>);
}

/**
 * 根据请求方法和参数构建URL和请求体
 * @param params - 请求参数
 * @param headers - 请求头
 * @returns 包含URL和请求体的对象
 */
function buildRequestUrlAndBody(params: IParams, headers: Record<string, string>) {
    let url: string;
    let body: BodyInit | undefined;

    if (params.method === METHOD.Get) {
        url = utils.replace.spliceToUrlByJson(params.url, params.requestBody ?? {});
        body = undefined;
    } else {
        url = params.url;
        const contentType = headers[HEADER.ContentType];
        body = processRequestBody(params.requestBody, contentType);
    }

    return { url, body };
}


/**
 * 根据Content-Type处理请求体
 * @param body - 原始请求体
 * @param contentType - Content-Type值
 * @returns 处理后的请求体
 */
function processRequestBody(body: object | undefined, contentType: string): BodyInit | undefined {
    switch (contentType) {
        case CONTENT_TYPE.ApplicationJson:
            return JSON.stringify(body);
        case CONTENT_TYPE.ApplicationWWW:
            return utils.to.jsonToQueryString(body);
        case CONTENT_TYPE.FromData:
            return body as BodyInit;
        default:
            return undefined;
    }
}

/**
 * 执行实际的Fetch请求
 * @param params - 请求参数
 * @param url - 请求URL
 * @param body - 请求体
 * @param headers - 请求头
 * @param callbackParams - 回调参数
 * @param logger - 日志记录器
 * @returns 返回Response对象
 */
// 顶层：可注入字段白名单 + 选择器（避免块级 function 声明）
const ADDITIONAL_FETCH_INIT_KEYS = [
    'duplex',
    'credentials',
    'mode',
    'cache',
    'redirect',
    'referrer',
    'referrerPolicy',
    'integrity',
    'keepalive',
    'priority',
    'reactNative',
] as const;

const pickAdditionalFetchInit = (params: IParams): Partial<RequestInit> => {
    const src = params?.requestConfig?.fetchOptions ?? params?.requestConfig?.fetchInit;
    if (!src || typeof src !== 'object') return {};
    const out: Partial<RequestInit> = {};
    for (const k of ADDITIONAL_FETCH_INIT_KEYS) {
        if (k in src) (out as any)[k] = (src as any)[k];
    }
    return out;
};

async function executeFetchRequest(
    params: IParams,
    url: string,
    body: BodyInit | undefined,
    headers: HeadersInit,
    callbackParams: ICallbackParams,
    logger: ILogger
): Promise<Response> {
    const controller = new AbortController();
    const signal = controller.signal;

    // 处理回调
    if (callbackParams?.requestInstance) {
        callbackParams.requestInstance(controller);
    }

    // 设置超时
    let timeoutId: any | undefined;
    if (params.timeout) {
        timeoutId = setTimeout(() => {
            logger.info('请求超时');
            logger.warn('url', url);
            logger.warn('body', body);
            controller.abort();
        }, params.timeout);
    }

    try {
        const extraInit = pickAdditionalFetchInit(params);
        const response = await fetch(url, {
            ...extraInit,
            method: params.method,
            headers,
            body,
            signal,
        });

        if (timeoutId) clearTimeout(timeoutId);

        return response;
    } catch (error) {
        if (timeoutId) clearTimeout(timeoutId);
        throw error;
    }
}

/**
 * 处理Fetch响应
 * @param response - Fetch响应对象
 * @param params - 请求参数
 * @param logger - 日志记录器
 * @returns 返回处理后的数据
 */
// 函数：handleFetchResponse
async function handleFetchResponse<T>(response: Response, params: IParams, logger: ILogger): Promise<T> {
    if (!response.ok) {
        const errorKey = `err_${response.status}` as keyof IRequestError;
        const error: RequestError = ERROR[errorKey] || ERROR.err_10000;
        error.method = params.method;
        error.url = response.url;
        error.params = params.requestBody;
        error.headers = params.headers;

        // 改进：尽力解析服务端错误体并放入 error.error
        try {
            const ct = response.headers.get('content-type') || '';
            if (ct.includes('application/json')) {
                error.error = await response.clone().json();
            } else {
                error.error = await response.clone().text();
            }
        } catch (parseErr: any) {
            logger.warn('解析失败响应体失败', parseErr);
            error.error = 'Unknown fetch error';
        }

        throw error;
    }

    // 如果需要数据流，直接返回 ReadableStream（SSE 使用此路径）
    if (params.responseType === 'stream') {
        logger.info('成功获取响应数据流');
        return response.body as T;
    }

    const contentType = response.headers.get('content-type') || '';

    if (contentType.includes('application/json')) {
        const data = await response.json();
        logger.info('成功解析JSON响应', { data });
        return data as T;
    } else if (contentType.includes('text/')) {
        const text = await response.text();
        logger.info('成功解析文本响应', { text });
        return text as T;
    } else if (contentType.includes('multipart/form-data')) {
        const formData = await response.formData();
        logger.info('成功解析表单数据');
        return formData as T;
    } else if (contentType.includes('application/octet-stream')) {
        const blob = await response.blob();
        logger.info('成功解析二进制数据');
        return blob as T;
    } else {
        logger.warn('未知的响应类型', { contentType });
        return undefined as T;
    }
}


export default sendFetchRequest;
